#include "stddef.h"
#include "string.h"
#include "cpuid.h"
#include "text_video.h"
#include "stdio.h"
#include "mm.h"

size_t base;
volatile size_t num_freed_blocks;

static struct memdesc
{
    size_t base;
    size_t size;
} memdesc[MAX_FREE];


void init_mm(void)
{
    size_t i;
    
    num_freed_blocks = 0;

    for(i = 0; i < MAX_FREE; i++)
    {memdesc[i].base = memdesc[i].size = 0;}

    base = (size_t)&kernel_end;
    
    debug_printf("kernel loaded at %x-%x (%uKB) \n\n", (size_t)&kernel_start, (size_t)&kernel_end, ((size_t)&kernel_end - (size_t)&kernel_start)/1024);
}

void * malloc(const size_t count)
{
    size_t i, found_free_blocks;
    size_t block;
    
    if(!count)
    {
        warning_printf("SYS: attempted memalloc of size 0\n");
        return (void *)(-1);
    }
    
    if(count >= 0x4000000)
    {
        warning_printf("SYS: attempted memalloc of a size >= 64M\n");
        return (void *)(-1);
    }
    
    found_free_blocks = 0;
    
    if(num_freed_blocks)
    {
        for(i = 0; i < MAX_FREE; i++)
        {
            if(memdesc[i].base)
            {
                if(memdesc[i].size == count)
                {
                    block = memdesc[i].base;
                    memdesc[i].base = 0;
                    memdesc[i].size = 0;
                    num_freed_blocks--;
                    return (void *)block;
                }
                
                if(memdesc[i].size > count)   
                {
                    block = memdesc[i].base;
                    memdesc[i].base += count;
                    memdesc[i].size -= count;
                    return (void *)block;
                }
            }
        }
    }
    
    block = base;
    base += count;
    
    return (void *)block;
}

bool free(const void * const mem, const size_t count)
{
    size_t i;

    if(!count)
    {return false;}
    
    if(!mem)
    {
        warning_printf("SYS: cannot memfree memory location %x\n", (size_t)(0));
        return false;
    }
    
    if((size_t)mem < 0x100000)
    {
        warning_printf("SYS: cannot memfree memory below 1MB (%x)\n", (size_t)(mem));
        return false;
    }
    
    if(((size_t)mem + count) >= (size_t)&kernel_start && (size_t)mem < (size_t)&kernel_end)
    {
        warning_printf("SYS: cannot memfree kernel memory space\n");
        return false;
    }
    
    if(num_freed_blocks >= MAX_FREE-1)
    {
        warning_printf("SYS: no more free slots for use by memfree, ignoring...\n");
        return false;
    }
    
    for(i = 0; i < MAX_FREE; i++)
    {
        if(!memdesc[i].base && !memdesc[i].size)
        {
            memdesc[i].base = (size_t)mem;
            memdesc[i].size = count;
            num_freed_blocks++;
            return true;
        }
    }

    return false;
}
